<?php
/**
 * Qi_Console_Tabular 
 *
 * @package Qi
 */

/**
 * Qi_Console_Tabular class
 *
 * An object for display tabular data in the console
 * 
 * @package Qi
 * @author Jansen Price <jansen.price@gmail.com> 
 * @version $Id$
 */
class Qi_Console_Tabular
{
    /**
     * Table data
     * 
     * @var array
     */
    protected $data = array();

    /**
     * Headers
     * 
     * @var array
     */
    protected $headers = array();

    /**
     * Columns
     * 
     * @var array
     */
    protected $cols = array();

    /**
     * Options
     * 
     * @var array
     */
    private $_options = array();

    /**
     * Constructor
     * 
     * @param array $data Table data to display
     * @param array $options Additional options to set
     *  Supported options:
     *      'headers'     - an indexed array of each header column
     *      'cellpadding' - how many spaces for the cellpadding
     *      'cellalign'   - an indexed array of the alignment option
     *                      for each column. Possible values are 'L', 'R'
     * @return void
     */
    public function __construct($data = null, $options = array())
    {
        if (null != $data) {
            $this->data = $data;
        }

        $this->_options = array(
            'cellpadding' => '2',
        );

        $this->_parseOptions($options);
    }

    /**
     * _parseOptions 
     * 
     * @param array $options Options to parse
     * @return void
     */
    protected function _parseOptions($options)
    {
        if (count($options)) {
            foreach ($options as $key => $value) {
                switch($key) {
                case 'headers':
                    $this->headers = $value;
                    break;
                case 'cellpadding':
                    $this->_options['cellpadding'] = $value;
                    break;
                case 'cellalign':
                    $this->_options['cellalign'] = $value;
                    break;
                }
            }
        }
    }

    /**
     * Render and display the table
     * 
     * @param bool $buffer Whether to buffer and return output
     * @return mixed
     */
    public function display($buffer = false)
    {
        if (!$this->data) {
            return;
        }

        $out = '';

        $this->_determineColumnWidths();

        $padding = str_repeat(" ", $this->_options['cellpadding']);

        $headerContent = '';
        $rowsepString  = '';

        if (count($this->headers)) {
            $headerContent .= "|";
            for ($i = 0; $i < count($this->headers); $i++) {
                $string = $padding
                    . str_pad($this->headers[$i], $this->cols[$i])
                    . $padding
                    . "|";

                $headerContent .= $string;
            }

            $rowsepString = "+"
                . str_repeat("-", strlen($headerContent) - 2) . "+";

            $out .= $rowsepString . "\n" . $headerContent
                . "\n" . $rowsepString . "\n";
        }

        $tableContent = '';
        foreach ($this->data as $row) {
            $content = '|';
            $i       = 0;

            foreach ($row as $col) {
                $padType = $this->getPadTypeForCol($i);
                $string  = $padding
                    . str_pad(trim($col), $this->cols[$i], ' ', $padType)
                    . $padding
                    . "|";

                $content .= $string;
                $i++;
            }

            $tableContent .= $content . "\n";

            if (!$rowsepString) {
                $rowsepString = "+" . str_repeat("-", strlen($content)-2) . "+";
            }
        }

        $out .= $tableContent . $rowsepString . "\n";

        if ($buffer) {
            return $out;
        } else {
            echo $out;
        }
    }

    /**
     * _determineColumnWidths 
     * 
     * @return void
     */
    protected function _determineColumnWidths()
    {
        $hc = count($this->headers);
        if ($hc) {
            for ($i = 0; $i < $hc; $i++) {
                $this->_setColumnWidth($i, strlen(trim($this->headers[$i])));
            }
        }

        $dc = count($this->data);
        if ($dc) {
            for ($r = 0; $r < $dc; $r++) {
                $c = 0;
                //for ($c = 0; $c < count($this->data[$r]); $c++) {
                foreach ($this->data[$r] as $col) {
                    $this->_setColumnWidth(
                        $c, strlen(trim($col))
                    );
                    $c++;
                }
            }
        }
    }

    /**
     * Set a column width
     * 
     * @param int $col Column id
     * @param int $width Width
     * @return void
     */
    protected function _setColumnWidth($col, $width)
    {
        $cols = $this->cols;
        if (isset($cols[$col])) {
            if ($width > $cols[$col]) {
                $cols[$col] = $width;
            }
        } else {
            $cols[$col] = $width;
        }

        $this->cols = $cols;
    }

    /**
     * Get the pad type for a column
     * 
     * @param int $index Index of column
     * @return int Constant for strpad() function
     */
    public function getPadTypeForCol($index)
    {
        $padType = STR_PAD_RIGHT;

        if (isset($this->_options['cellalign'])
            && isset($this->_options['cellalign'][$index])
        ) {
            switch ($this->_options['cellalign'][$index]) {
            case 'R':
                $padType = STR_PAD_LEFT;
                break;
            case 'L': //pass through
            default:
                $padType = STR_PAD_RIGHT;
            }
        }

        return $padType;
    }
}
